﻿/*
 * @author: wizardc
 */

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;

namespace Dou.UI
{
    /// <summary>
    /// 以圆形或圆环展示的图片
    /// </summary>
    [ExecuteInEditMode]
    public class CircleImage : DImage
    {
        [SerializeField]
        private int _segmentCount = 36;
        [SerializeField]
        private float _fillPercent = 1;
        [SerializeField]
        private bool _overrideRaycast = true;
        
        private List<Vector3> _vertexList;
        
        /// <summary>
        /// 圆形由多少个片段构成
        /// </summary>
        public int segmentCount
        {
            set
            {
                if (_segmentCount != value)
                {
                    _segmentCount = value;
                    SetVerticesDirty();
                }
            }
            get => _segmentCount;
        }

        /// <summary>
        /// 填充百分比
        /// </summary>
        public float fillPercent
        {
            set
            {
                if (_fillPercent != value)
                {
                    _fillPercent = value;
                    SetVerticesDirty();
                }
            }
            get => _fillPercent;
        }

        /// <summary>
        /// 是否重写点击测试，使响应区域和圆形区域一致
        /// </summary>
        public bool overrideRaycast
        {
            set => _overrideRaycast = value;
            get => _overrideRaycast;
        }

        protected override void OnPopulateMesh(VertexHelper toFill)
        {
            toFill.Clear();
            
            _vertexList = new List<Vector3>();

            float width = rectTransform.rect.width;
            float height = rectTransform.rect.height;
            int realSegment = (int)(_segmentCount * _fillPercent);

            Vector4 uv = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;

            float uvWidth = uv.z - uv.x;
            float uvHeight = uv.w - uv.y;

            Vector2 uvCenter = new Vector2(uvWidth * 0.5f, uvHeight * 0.5f);

            Vector2 converRatio = new Vector2(uvWidth / width, uvHeight / height);

            float radian = (Mathf.PI * 2) / _segmentCount;
            float radius = width * 0.5f;

            // 中心顶点坐标
            Color32 originColor;
            if (_fillPercent < 1)
            {
                originColor = new Color32(60, 60, 60, 255);
            }
            else
            {
                originColor = color;
            }
            // 处理中心点移动圆形跟着动的 bug
            Vector3 originPos = new Vector3((0.5f - rectTransform.pivot.x) * width, (0.5f - rectTransform.pivot.y) * height);
            Vector3 uvOriginPos = Vector3.zero;
            UIVertex originVertex = GetUIVertex(originColor, originPos, uvOriginPos, uvCenter, converRatio);
            toFill.AddVert(originVertex);

            // 创建其它位于圆上的顶点
            int vertexCount = realSegment + 1;
            float curRadian = 0;
            for (int i = 0; i < _segmentCount + 1; i++)
            {
                float x = Mathf.Cos(curRadian) * radius;
                float y = Mathf.Sin(curRadian) * radius;

                curRadian += radian;

                Color32 tempColor;
                if (i < vertexCount)
                {
                    tempColor = color;
                }
                else
                {
                    tempColor = new Color32(60, 60, 60, 255);
                }
                Vector3 tempPos = new Vector3(x, y);
                UIVertex vertexTemp = GetUIVertex(tempColor, originPos + tempPos, tempPos, uvCenter, converRatio);

                // 将外围的所有点的位置进行添加
                _vertexList.Add(vertexTemp.position);

                toFill.AddVert(vertexTemp);
            }

            int id = 1;
            for (int i = 0; i < _segmentCount; i++)
            {
                toFill.AddTriangle(id, 0, id + 1);
                id++;
            }
        }
        
        private UIVertex GetUIVertex(Color32 color, Vector3 pos, Vector2 uvPos, Vector2 uvCenter, Vector2 uvScale)
        {
            UIVertex uiVertex = new UIVertex();
            uiVertex.color = color;
            uiVertex.position = pos;
            uiVertex.uv0 = new Vector2(uvPos.x * uvScale.x + uvCenter.x, uvPos.y * uvScale.y + uvCenter.y);
            return uiVertex;
        }
        
        public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
        {
            if (_overrideRaycast)
            {
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out var localPoint);
                return IsValid(localPoint);
            }
            return base.IsRaycastLocationValid(screenPoint, eventCamera);
        }
        
        private bool IsValid(Vector2 localPoint)
        {
            // 交点数量是奇数的情况下表明点击坐标位于图形内
            return GetCrossPointNum(localPoint, _vertexList) % 2 == 1;
        }

        private int GetCrossPointNum(Vector2 localPoint, List<Vector3> vertexList)
        {
            // 和与 x 轴平行的向右（→）的直线判断交点
            int result = 0;
            Vector3 vect1 = Vector3.zero;
            Vector3 vect2 = Vector3.zero;
            int count = vertexList.Count;
            for (int i = 0; i < count; i++)
            {
                vect1 = vertexList[i];
                vect2 = vertexList[(i + 1) % count];
                if (IsInYRange(localPoint, vect1, vect2))
                {
                    // 如果交点的 x 值大于点击的 x 值，表示和向右（→）的直线相交
                    if (localPoint.x < GetXInLine(vect1, vect2, localPoint.y))
                    {
                        result++;
                    }
                }
            }
            return result;
        }

        // 点击的 y 必须位于线段的 y 轴区域中，否则不会发生相交
        private bool IsInYRange(Vector2 localPoint, Vector3 vect1, Vector3 vect2)
        {
            if (vect1.y > vect2.y)
            {
                return localPoint.y < vect1.y && localPoint.y > vect2.y;
            }
            return localPoint.y < vect2.y && localPoint.y > vect1.y;
        }

        // 获取线段上指定 y 轴的 x 轴坐标
        private float GetXInLine(Vector3 vect1, Vector3 vect2, float y)
        {
            float k = (vect1.y - vect2.y) / (vect1.x - vect2.x);
            return vect1.x + (y - vect1.y) / k;
        }
    }
}
